//
// Created by tangjian on 10/12/23.
//
#include "head.h"
#include "amd5.h"

int file_recvn(int sockfd, void *buf, int n) {
    int total = 0;
    char *p = (char *) buf;
    while (total < n) {
        ssize_t sret = recv(sockfd, p + total, n - total, 0);
        total += sret;
    }
    return 0;
}

int cTransFile(int sockfd, trans_type transt, char *cwp) {
    int ret = 1;

    workFile_t *wf = (workFile_t *) calloc(1, sizeof(workFile_t));
    int ret_wf_init = cWorkFile_init(wf, cwp);

    switch (transt) {
        case SENDF:ret = tSendFile(sockfd, wf);
            break;
        case RECVF:ret = tRecvFile(sockfd, wf);
            break;
        default:break;
    }

    free(wf);

    //发送结束
//    sendCMDEnd(sockfd);
    return 0;
}

int cWorkFile_init(workFile_t *wf, char *filepath) {
    //设置文件名
    char *fileName = getFileName(filepath);
    memcpy(wf->filename, fileName, strlen(fileName));

    //设置文件路径
    memcpy(wf->filepath, filepath, strlen(filepath));

    if (file_exist(wf->filepath)) {
        //设置文件大小
        struct stat statBuf;
        stat(wf->filepath, &statBuf);
        wf->filesize = statBuf.st_size;
        //设置md5
        char filemd5[MD5_STR_LEN+1] = {0};
        int ret = Compute_file_md5(wf->filepath, filemd5);
        memcpy(wf->md5, filemd5, strlen(filemd5));
    } else {
        wf->filesize = 0;
    }

    //设置偏移量
    wf->offset = 0;

    return 1;
}

int sendCMDEnd(int netfd) {
    //发送
    train_t train;
    train.length = 0;
    memcpy(train.data, train.data, train.length);
    send(netfd, &train, sizeof(int) + train.length, MSG_NOSIGNAL);
    return 1;
}

//cwp : current work path
int workFile_init(int netfd, workFile_t *wf, char *cwp) {

    //接收目标文件路径
    train_t tfilepath;
    file_recvn(netfd, &tfilepath.length, sizeof(int));
    file_recvn(netfd, tfilepath.data, tfilepath.length);

    //设置文件名
    char *fileName = getFileName(cwp);
    memcpy(wf->filename, fileName, strlen(fileName));

    //设置文件路径
    char filepath[1024];
    sprintf(wf->filepath, "%s%s%s", cwp, "/", fileName);

    //设置文件大小
    if (file_exist(wf->filepath)) {
        struct stat statBuf;
        stat(wf->filepath, &statBuf);
        wf->filesize = statBuf.st_size;
    } else {
        wf->filesize = 0;
    }

    //设置偏移量
    wf->offset = 0;

    return 1;
}

bool file_exist(char *filepath) {
    if (access(filepath, F_OK) == 0) {
        //printf("File exists.: %s\n", filepath);
        return true;
    } else {
        //printf("File does not exist.: %s\n", filepath);
        return false;
    }
}

char *getFileName(char *filepath) {
    char *fileName = NULL;
    char *lastSlash = strrchr(filepath, '/'); // 查找最后一个斜杠字符'/'

    if (lastSlash != NULL) {
        fileName = strdup(lastSlash + 1); // 复制斜杠后的字符串作为文件名
    } else {
        fileName = strdup(filepath); // 如果没有斜杠，则整个路径都是文件名
    }

    return fileName;
}

int sendFileData(int netfd, train_t *train) {
    //发送
    send(netfd, train, sizeof(int) + train->length, MSG_NOSIGNAL);
    return 1;
}

int recvFileDta(int netfd, train_t *train) {
    //接收文件数据
    file_recvn(netfd, &train->length, sizeof(int));
    file_recvn(netfd, &train->data, train->length);
    return train->length;
}

int recvFileInfo(int netfd, workFile_t *wf) {
    train_t train_wf;
    file_recvn(netfd, &train_wf.length, sizeof(int));
    file_recvn(netfd, &train_wf.data, train_wf.length);
    memcpy(wf, train_wf.data, sizeof(workFile_t));
    return 0;
}

int sendFileInfo(int netfd, workFile_t *wf) {
    train_t train;
    train.length = sizeof(workFile_t);
    memcpy(train.data, wf, train.length);
    send(netfd, &train, sizeof(int) + train.length, MSG_NOSIGNAL);
    return 1;
}

int sendPrintInfo(int netfd, char *info) {
    int ret_sendInfo = send(netfd, info, strlen(info), MSG_NOSIGNAL);
    ERROR_CHECK(ret_sendInfo, -1, "send to client info");
    return 1;
}

int tSendFile(int netfd, workFile_t *wf) {
    //发送本地文件信息
    sendFileInfo(netfd, wf);

    //接收确认文件信息
    workFile_t rwf;
    recvFileInfo(netfd, &rwf);

    //如果服务器秒传完成结束
    if(rwf.s_flag == 1){
        //秒传完成结束
        return 1;
    }

    int fd = open(wf->filepath, O_RDWR);
    if (rwf.offset)
        lseek(fd, rwf.offset, SEEK_SET);
    ssize_t data_size = rwf.filesize - rwf.offset;

    //预计发送数据
    ssize_t perSendData = rwf.filesize - rwf.offset;
    //发送文件数据
    train_t train;
    char *start;
    start = (char *) mmap(NULL, wf->filesize, PROT_READ, MAP_SHARED, fd, 0);
    char *cur;
    cur = start + rwf.offset;
    if (wf->filesize <= 100 * 1024 * 1024) {
        while (perSendData) {
            bzero(&train, sizeof(train));
            //文件片大小为单位传送
            if (perSendData > FILE_PIECE_SIZE) {
                train.length = FILE_PIECE_SIZE;
                perSendData -= FILE_PIECE_SIZE;
            } else {
                train.length = perSendData;
                perSendData = 0;
            }

            memcpy(train.data, cur, train.length);
            cur += train.length;
            sendFileData(netfd, &train);
        }
        munmap(start, wf->filesize);
    } else {
        while (perSendData) {
            bzero(&train, sizeof(train));
            //文件片大小为单位传送
            if (perSendData > FILE_PIECE_SIZE) {
                train.length = FILE_PIECE_SIZE;
                perSendData -= FILE_PIECE_SIZE;
            } else {
                train.length = perSendData;
                perSendData = 0;
            }

            read(fd, &train.data, train.length);
            cur += train.length;
            sendFileData(netfd, &train);
        }
    }
    close(fd);
//    sendCMDEnd(netfd);
    return 0;
}

int tRecvFile(int netfd, workFile_t *wf) {
    //接收远程文件信息
    workFile_t rwf;
    recvFileInfo(netfd, &rwf);

    if (rwf.s_flag == 2) {
        printf("文件不存在!\n");
        return 1;
    }

    //确认本地信息
    int fd = open(wf->filepath, O_RDWR | O_CREAT, 0666);
    if (wf->filesize != 0) {
        rwf.offset = lseek(fd, 0, SEEK_END);
    }

    //发送本地确认信息
    sendFileInfo(netfd, &rwf);

    //预计接收长度
    ssize_t preDataSize = rwf.filesize - wf->filesize;

    //开始接收
    if (preDataSize > 0) {
        if (rwf.filesize <= 100 * 1024 * 1024) {
            ftruncate(fd, rwf.filesize);
            char *start;
            start = (char *) mmap(NULL, rwf.filesize, PROT_WRITE, MAP_SHARED, fd, 0);
            char *cur = start + rwf.offset;

            train_t train;
            while (1) {
                bzero(&train, sizeof(train));
                recvFileDta(netfd, &train);
                memcpy(cur, train.data, train.length);
                cur += train.length;
                preDataSize -= train.length;
                if (preDataSize <= 0 || train.length ==0)  {
                    break;
                }
            }
            munmap(start, rwf.filesize);
            ftruncate(fd, rwf.filesize - preDataSize);
        } else {
            train_t train;
            bzero(&train, sizeof(train));

            while (!recvFileDta(netfd, &train)) {
                write(fd, train.data, train.length);
                preDataSize -= train.length;
                if (preDataSize <= 0 || train.length ==0)  {
                    break;
                }
                bzero(&train, sizeof(train));
            }
        }
    }
    close(fd);
    return 0;
}
